//
//  MZEndpoint.swift
//  MZTool
//
//  Created by gby on 2023/2/9.
//

import Foundation

open class MZEndpoint {
    
    public let url: String
    
    public let method: MZMethod
    
    public let task: MZTask
    
    public let httpHeaderFields: [String: String]?
    
    public init(url: String, method: MZMethod, task: MZTask, httpHeaderFields: [String : String]?) {
        self.url = url
        self.method = method
        self.task = task
        self.httpHeaderFields = httpHeaderFields
    }
    
}


extension MZEndpoint {
    
    func urlRequest() throws -> URLRequest {
        guard let requestURL = Foundation.URL(string: url) else {
            throw MZError.requestMapping(url)
        }
        
        var request = URLRequest(url: requestURL)
        request.httpMethod = method.rawValue
        request.allHTTPHeaderFields = httpHeaderFields
        
        switch task {
        case .requestPlain, .uploadFile, .uploadMultipart, .downloadDestination:
            return request
        case .requestData(let data):
            request.httpBody = data
            return request
        case .requestJSONEncodable(let encodable):
            return try request.encoded(encodable: encodable)
        case .requestCustomJSONEncodable(let encodable, encoder: let encoder):
            return try request.encoded(encodable: encodable, encoder: encoder)
        case .requestParameter(parameters: let parameters, encoding: let encoding):
            return try request.encoded(parameters: parameters, parameterEncoding: encoding)
        case .uploadCompositeMultipart(_, urlParameters: let urlParameters):
            let parameterEncoding = MZURLEncoding(destination: .queryString)
            return try request.encoded(parameters: urlParameters, parameterEncoding: parameterEncoding)
        case .downloadParameters(parameters: let parameters, encoding: let encoding, _):
            return try request.encoded(parameters: parameters, parameterEncoding: encoding)
        case .requestCompositeData(bodyData: let bodyData, urlParameters: let urlParameters):
            request.httpBody = bodyData
            let parameterEncoding = MZURLEncoding(destination: .queryString)
            return try request.encoded(parameters: urlParameters, parameterEncoding: parameterEncoding)
        case .requestCompositeParameters(bodyParameters: let bodyParameters, bodyEncoding: let bodyEncoding, urlParameters: let urlParameters):
            if  let bodyParameterEncoding =  bodyEncoding as? MZURLEncoding, bodyParameterEncoding.destination != .httpBody {
                fatalError("Only URLEncoding that `bodyEncoding` accepts is URLEncoding.httpBody. Others like `default`, `queryString` or `methodDependent` are prohibited - if you want to use them, add your parameters to `urlParameters` instead")
            }
            let bodyfulRequest = try request.encoded(parameters: bodyParameters, parameterEncoding: bodyEncoding)
            let urlEncoding = MZURLEncoding(destination: .queryString)
            return try bodyfulRequest.encoded(parameters: urlParameters, parameterEncoding: urlEncoding)
        }
    }
    
}


//extension MZEndpoint: Equatable, Hashable {
//    public func hash(into hasher: inout Hasher) {
//        switch task {
//        case let .uploadFile(file):
//            hasher.combine(file)
//        case let .uploadMultipart(multipartData), let .uploadCompositeMultipart(multipartData, _):
//            hasher.combine(multipartData)
//        default:
//            break
//        }
//
//        if let request = try? urlRequest() {
//            hasher.combine(request)
//        } else {
//            hasher.combine(url)
//        }
//    }
//
//    /// Note: If both Endpoints fail to produce a URLRequest the comparison will
//    /// fall back to comparing each Endpoint's hashValue.
//    public static func == (lhs: MZEndpoint, rhs: MZEndpoint) -> Bool {
//        let areEndpointsEqualInAdditionalProperties: Bool = {
//            switch (lhs.task, rhs.task) {
//            case (let .uploadFile(file1), let .uploadFile(file2)):
//                return file1 == file2
//            case (let .uploadMultipart(multipartData1), let .uploadMultipart(multipartData2)),
//                 (let .uploadCompositeMultipart(multipartData1, _), let .uploadCompositeMultipart(multipartData2, _)):
//                return multipartData1 == multipartData2
//            default:
//                return true
//            }
//        }()
//        let lhsRequest = try? lhs.urlRequest()
//        let rhsRequest = try? rhs.urlRequest()
//        if lhsRequest != nil, rhsRequest == nil { return false }
//        if lhsRequest == nil, rhsRequest != nil { return false }
//        if lhsRequest == nil, rhsRequest == nil { return lhs.hashValue == rhs.hashValue && areEndpointsEqualInAdditionalProperties }
//        return lhsRequest == rhsRequest && areEndpointsEqualInAdditionalProperties
//    }
//}
